using System;
using System.Collections;
using System.Reflection;
using System.Xml;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using System.IO;
using System.Collections.Generic;
using System.Diagnostics;

namespace BizTalk.BuildGenerator.Tasks
{
    /// <remarks />
    public class GenerateTasksFile : Task
    {                
        private ITaskItem[] _assemblyPaths;     
        private string _targetsFilePath;
        private string _assemblyPathPrefix;
        private const string _msbuildNamespace = "http://schemas.microsoft.com/developer/msbuild/2003";
               
        /// <summary>
        /// Any prefix before the assembly name
        /// </summary>
        public string AssemblyPathPrefix
        {
            get { return _assemblyPathPrefix; }
            set { _assemblyPathPrefix = value; }
        }
        /// <summary>
        /// The list of assemblies to inspect
        /// </summary>
        [Required]
        public ITaskItem[] AssemblyPaths
        {
            get { return _assemblyPaths; }
            set { _assemblyPaths = value; }
        }

        /// <remarks />
        [Required]
        public string TargetsFilePath
        {
            get { return _targetsFilePath; }
            set { _targetsFilePath = value; }
        }
        
        /// <summary>
        /// Implements the execute method
        /// </summary>
        /// <returns></returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "Microsoft.Build.Utilities.TaskLoggingHelper.LogWarning(System.String,System.Object[])")]
        public override bool Execute()
        {
            try
            {
                string tasksFilePath = Path.GetFullPath(this._targetsFilePath);

                XmlDocument targetsFile = new XmlDocument();
                XmlDeclaration xmlDeclaration = targetsFile.CreateXmlDeclaration("1.0", "utf-8", null);
                XmlElement rootElement = targetsFile.CreateElement("Project");
                rootElement.SetAttribute("xmlns", _msbuildNamespace);
                targetsFile.AppendChild(xmlDeclaration);
                targetsFile.AppendChild(rootElement);
                foreach (ITaskItem assemblyPathItem in AssemblyPaths)
                {
                    string assemblyPath = assemblyPathItem.ItemSpec;
                    string assemblyAbsolutePath = Path.GetFullPath(assemblyPath);
                    Assembly assembly = Assembly.Load(File.ReadAllBytes(assemblyAbsolutePath));                    
                    string[] taskNames = GetMSBuildTaskNamesFromAssembly(assembly);
                    foreach (string taskName in taskNames)
                    {
                        XmlElement usingTaskElement = targetsFile.CreateElement("UsingTask");
                        XmlAttribute taskNameAttrib = targetsFile.CreateAttribute("TaskName");
                        taskNameAttrib.InnerText = taskName;
                        XmlAttribute assemblyFileAttrib = targetsFile.CreateAttribute("AssemblyFile");
                        if (string.IsNullOrEmpty(this._assemblyPathPrefix))
                            assemblyFileAttrib.InnerText = assembly.GetName().Name + ".dll";
                        else
                            assemblyFileAttrib.InnerText = string.Format(@"{0}\{1}.dll", this._assemblyPathPrefix, assembly.GetName().Name);

                        usingTaskElement.Attributes.Append(taskNameAttrib);
                        usingTaskElement.Attributes.Append(assemblyFileAttrib);
                        rootElement.AppendChild(usingTaskElement);
                    }                    
                }
                using (FileStream fs = new FileStream(tasksFilePath, FileMode.Create, FileAccess.Write))
                {
                    targetsFile.Save(fs);
                    fs.Flush();
                }

                if (rootElement.ChildNodes.Count < 1) // No MSBuild Tasks have been found
                {
                    if (BuildEngine != null)
                        Log.LogWarning("No MSBuild Tasks were found in the supplied assembly list.");                    
                }
                return true;
            }
            catch (Exception ex)
            {
                Log.LogError(ex.ToString());
                return false;
            }
        }
        /// <summary>
        /// Gets a list of the typenames which derive from Task
        /// </summary>
        /// <param name="assembly"></param>
        /// <returns></returns>
        private static string[] GetMSBuildTaskNamesFromAssembly(Assembly assembly)
        {
            try
            {                
                Type[] types = assembly.GetTypes();
                ArrayList taskTypeList = new ArrayList();
                foreach (Type type in types)
                {
                    if (type.IsSubclassOf(typeof(Task)))
                        taskTypeList.Add(type);
                }
                Type[] taskTypes = (Type[])taskTypeList.ToArray(typeof(Type));
                string[] taskNames = new string[taskTypes.Length];
                for (int i = 0; i < taskTypes.Length; i++)
                    taskNames[i] = taskTypes[i].FullName;
                return taskNames;
            }
            catch (ReflectionTypeLoadException ex)
            {
                Trace.WriteLine(ex.ToString());
                foreach(Exception e in ex.LoaderExceptions)
                    Trace.WriteLine(e.ToString());
                
                return null;
            }
        }       
    }
}